#ifndef __BLOQUE_SEQUENCE_SET_H__
#define __BLOQUE_SEQUENCE_SET_H__

#include "Bloque.h"
#include <map>

using namespace std;

template<class Clave,class Dato>
class BloqueSequenceSet;

template <class Clave,class Dato>
ostream &operator<<(ostream &os,BloqueSequenceSet<Clave,Dato> &bloque);

template<class Clave,class Dato>
class BloqueSequenceSet:public Bloque<Clave>
{

public:

	/**
	* Se crea un bloque del orden especificado.
	* @param orden Orden del bloque a crear.
	*/
	BloqueSequenceSet(const short orden=0):
	Bloque<Clave>(orden)
	{
	}
	
	BloqueSequenceSet(const BloqueSequenceSet<Clave,Dato> &bloque):
	Bloque<Clave>(bloque),
	datos(bloque.datos)
	{
	}
	
	BloqueSequenceSet<Clave,Dato> &operator=(
		const BloqueSequenceSet<Clave,Dato> &bloque)
	{
		if (this!=(&bloque))
		{
			Bloque<Clave>::operator=(bloque);
			this->datos=bloque.datos;
		}
		
		return *this;
	}

	virtual ~BloqueSequenceSet()
	{
	}

	/**
	* Se busca la clave pasada. En caso de encontrarla se obtienen los datos
	* asociados a dicha clave. Sino se setea la marca de fin de archivo.
	* @param dato Dato recuperado si se encuentra la clave.
	* @param clave Clave a ser buscar.
	*/
	void readIgual(Dato &dato,const Clave &clave)
	{
		Bloque<Clave>::readIgual(clave);

		if (!(this->eof()))
			dato=this->getDato();
	}

	/**
	* Se obtiene el dato de la clave actual.
	* @return 
	*/
	Dato getDato()
	{
		if (this->eof())
			throw string("El cursor de la clave actual del bloque es invalido");

		return datos.find(this->getClaveActual())->second;
	}

	/**
	* Se inicializa this.
	*/
	virtual void clear()
	{
		Bloque<Clave>::clear();
		this->datos.clear();
	}

	/**
	* Se inserta el dato asociado a la clave pasada en el bloque.
	* @param clave Clave a ser agregada al arbol.
	* @param dato Dato a ser agregado en el arbol.
	*/
	void insert(const Clave &clave,const Dato &dato)
	{
		Bloque<Clave>::insert(clave);
		datos[clave]=dato;
	}

	/**
	* Se modifica el dato asociado a la clave pasada en el bloque.
	* @param clave Clave a ser modificada en al arbol.
	* @param dato Dato a ser modificado en el arbol.
	*/
	void update(const Clave &clave,const Dato &dato)
	{
		datos[clave]=dato;
	}

	/**
	* Se leen los datos del istream al bloque en cuestion.
	* @param is istream del cual se leeran los datos a partir de la
	* posicion en la que se encuentre el cursor.
	*/
	virtual void read(istream &is)
	{
		Bloque<Clave>::read(is); 

		typename multiset<Clave>::const_iterator it=this->claves.begin();
		
		// Se leen los datos.
		for (int i=0;i<=this->orden;i++)
		{
			Dato dato;

			dato.read(is);
			
			if (it!=this->claves.end())
			{
				this->datos[*it]=dato;
				it++;
			}
		}
	}

	/**
	* Se escriben los datos del bloque al ostream en cuestion.
	* @param os ostream en el cual se escribiran los datos a partir de la
	* posicion en la que se encuentre el cursor.
	*/
	virtual void write(ostream &os)
	{
		Bloque<Clave>::write(os);

		typename multiset<Clave>::const_iterator it=this->claves.begin();
		
		Dato dato;

		// Se obtienen los datos.
		for (int i=0;i<=this->orden;i++)
		{

			if (it!=this->claves.end())
			{
				try
				{
					this->datos.find(*it)->second.write(os);
				}
				catch(string s)
				{
					cout << "Claves: ";
					typename multiset<Clave>::const_iterator ic;
					for(ic=this->claves.begin();ic!=this->claves.end();ic++)
						cout << *ic << ",";
					cout << endl;
					 
					cout << "Datos: ";
					typename map<Clave,Dato>::const_iterator id;
					for(id=this->datos.begin();id!=this->datos.end();id++)
						cout << "(" << id->first << "," << id->second << ")" << ",";
					cout << endl;
					
					throw s;
				}
				it++;
			}
			else
				dato.write(os);
		}
	}

	/**
	* Se toman las claves de this junto con clave y se reparten entre this y
	* bloque asignando las claves menores a this y las claves mayores a bloque.
	* @param bloque Bloque nuevo resultado del split.
	* @param claveAscendente Clave a ser ascendida.
	* @param clave Clave a ser agregada.
	* @param dato Dato a ser asociado a la clave pasada.
	*/
	void split(BloqueSequenceSet<Clave,Dato> &bloque,Clave &claveAscendente,
		const Clave &clave,const Dato &dato)
	{
		Bloque<Clave>::split(bloque,claveAscendente,clave);

		Datos datos=this->datos;

		datos[clave]=dato;

		this->datos.clear();

		this->asignarDatos(datos);
		
		bloque.asignarDatos(datos);
	}

	/**
	 * Se elimina la clave pasada junto con el dato asociado a la misma, de 
	 * this.
	 * @param clave Clave a ser eliminada.
	 */
	virtual void erase(const Clave &clave)
	{
		Bloque<Clave>::erase(clave);
		
		this->datos.erase(clave);
	}
	
	void verificarDatos()
	{
		typename map<Clave,Dato>::iterator itd;
		
		if (this->claves.size()!=this->datos.size())
			throw string("La cantidad de claves no se corresponde con la cantidad de datos");
		
		this->first();
		
		while (!this->eof())
		{
			if (this->datos.find(this->getClaveActual())==this->datos.end())
				throw string("Inconsistencia en la asociacion de claves y datos");
			
			this->next();
		}
		
	}
	
	/**
	 * Se pasan las claves de bloque a this.
	 * @param bloque Bloque del cual se obtendran las claves.
	 */
	virtual void unir(Bloque<Clave> &bloque)
	{		
		Bloque<Clave>::unir(bloque);
		
		BloqueSequenceSet<Clave,Dato> &bloqueSeq=(
			BloqueSequenceSet<Clave,Dato> &)bloque;
			
		typename map<Clave,Dato>::const_iterator i;
		for(i=bloqueSeq.datos.begin();i!=bloqueSeq.datos.end();i++)
			this->datos[i->first]=i->second;
	}

protected:

	typedef map<Clave,Dato> Datos;

	Datos datos;

	/**
	* Se asignan los datos para las claves existentes en this.
	* @param datos Datos asociados a claves.
	*/
	void asignarDatos(const Datos &datos)
	{
		typename multiset<Clave>::const_iterator i;

		for(i=this->claves.begin();i!=this->claves.end();i++)
			this->datos[*i]=datos.find(*i)->second;
	}
	
	friend ostream &operator<<<Clave,Dato>(ostream &os,BloqueSequenceSet<Clave,Dato> &bloque);

};

template <class Clave,class Dato>
ostream &operator<<(ostream &os,BloqueSequenceSet<Clave,Dato> &bloque)
{
	bloque.first();

	os << "{";

	while (!bloque.eof())
	{
		os << "(" << bloque.getClaveActual() << "," << bloque.getDato() << ");";
		bloque.next();
	}

	os << "}";

	return os;
}

#endif
